/**
 * 性能监控工具
 * 用于收集和分析页面性能数据
 */

// 性能指标类型
interface PerformanceMetrics {
  pageLoadTime?: number // 页面加载时间
  firstPaint?: number // 首次绘制时间
  firstContentfulPaint?: number // 首次内容绘制时间
  domInteractive?: number // DOM可交互时间
  domComplete?: number // DOM完成时间
  resourceLoadTime?: Record<string, number> // 资源加载时间
  memoryUsage?: number // 内存使用情况
  customMarks?: Record<string, number> // 自定义性能标记
}

// 埋点事件数据类型
interface TrackEventData {
  buttonId?: string
  actionDesc?: string
  [key: string]: unknown // 允许扩展字段，但使用unknown更安全
}

// 埋点数据类型
interface TrackEvent {
  eventName: string // 事件名称
  eventType: string // 事件类型
  timestamp: number // 时间戳
  pageUrl: string // 页面URL
  userId?: string // 用户ID
  data?: TrackEventData // 额外数据
}

// 在文件顶部添加 Performance 扩展类型声明
interface PerformanceMemory {
  usedJSHeapSize: number;      // 已使用的 JS 堆内存
  totalJSHeapSize: number;     // 总 JS 堆内存
  jsHeapSizeLimit: number;     // JS 堆内存限制
}

// 扩展 Window 接口，添加 memory 属性
declare global {
  interface Window {
    performance: Performance & {
      memory?: PerformanceMemory;  // 可选属性，因为不是所有浏览器都支持
    };
  }
}

class PerformanceMonitor {
  private static instance: PerformanceMonitor
  private metrics: PerformanceMetrics = {}
  private events: TrackEvent[] = []
  private isInitialized: boolean = false
  private endpoint: string = ''
  private userId: string = ''
  private paintObserver?: PerformanceObserver

  private constructor() {}

  // 单例模式
  public static getInstance(): PerformanceMonitor {
    if (!PerformanceMonitor.instance) {
      PerformanceMonitor.instance = new PerformanceMonitor()
    }
    return PerformanceMonitor.instance
  }

  // 新增：获取用户ID（供父组件构建 env 数据）
  public getUserId(): string {
    return this.userId
  }

  // 已有：获取 metrics 和 events 的方法（确保存在）
  public getMetrics(): PerformanceMetrics {
    return { ...this.metrics }
  }

  public getEvents(): TrackEvent[] {
    return [...this.events]
  }

  // 初始化监控
  public init(config: { endpoint: string; userId?: string }): void {
    if (this.isInitialized) return

    this.endpoint = config.endpoint
    this.userId = config.userId || 'anonymous'
    this.isInitialized = true

    // 1. 监听页面加载完成事件（获取load相关指标）
    window.addEventListener('load', () => this.collectPageLoadMetrics())

    // 2. 监听FP/FCP（必须在页面加载早期执行，避免错过事件）
    this.observePaintMetrics()

    // 3. 监听页面离开事件
    window.addEventListener('beforeunload', () => this.sendData())

    console.log('Performance monitoring initialized')
  }
  private observePaintMetrics(): void {
    if (!window.PerformanceObserver) {
      console.warn('浏览器不支持PerformanceObserver，无法获取FP/FCP')
      return
    }

    // 监听'paint'类型的性能条目
    this.paintObserver = new PerformanceObserver((entries) => {
      entries.getEntries().forEach((entry) => {
        // 首次绘制（FP）
        if (entry.name === 'first-paint') {
          this.metrics.firstPaint = entry.startTime
          console.log(`✅ 捕获首次绘制(FP):${Math.round(entry.startTime)}ms`)
        }
        // 首次内容绘制（FCP）
        if (entry.name === 'first-contentful-paint') {
          this.metrics.firstContentfulPaint = entry.startTime
          console.log(
            `✅ 捕获首次内容绘制(FCP):${Math.round(entry.startTime)}ms`
          )
        }
      })
    })

    // 启动观测（指定类型为'paint'）
    this.paintObserver.observe({ type: 'paint', buffered: true })
  }
  // 收集页面加载性能指标
  private collectPageLoadMetrics(): void {
    if (!window.performance) return

    const timing = window.performance.timing
    const navigationStart = timing.navigationStart

    // 页面加载时间：loadEventEnd - navigationStart（必须在load事件后获取）
    if (timing.loadEventEnd > 0) {
      this.metrics.pageLoadTime = timing.loadEventEnd - navigationStart
      console.log(`✅ 页面加载时间：${Math.round(this.metrics.pageLoadTime)}ms`)
    } else {
      // 降级：如果loadEventEnd未就绪，用domComplete替代
      this.metrics.pageLoadTime = timing.domComplete - navigationStart
      console.log(
        `⚠️ 用domComplete替代页面加载时间:${Math.round(
          this.metrics.pageLoadTime
        )}ms`
      )
    }

    // DOM相关时间（可靠）
    this.metrics.domInteractive = timing.domInteractive - navigationStart
    this.metrics.domComplete = timing.domComplete - navigationStart

    // 收集资源和内存信息
    this.collectResourceTiming()
    this.collectMemoryUsage()

    console.log('Page load metrics collected', this.metrics)
  }

  // 收集资源加载时间
  private collectResourceTiming(): void {
    if (!window.performance || !window.performance.getEntriesByType) return

    const resources = window.performance.getEntriesByType('resource')
    const resourceTiming: Record<string, number> = {}

    resources.forEach((entry) => {
      // 安全断言：判断 entry 是否为 PerformanceResourceTiming 类型
      if (entry.entryType === 'resource' && 'duration' in entry) {
        const resource = entry as PerformanceResourceTiming // 此时断言安全
        const name = resource.name.split('/').pop() || resource.name
        resourceTiming[name] = resource.duration
      }
    })

    this.metrics.resourceLoadTime = resourceTiming
  }

  // 收集内存使用情况
  private collectMemoryUsage(): void {
    if (window.performance && window.performance.memory) {
      this.metrics.memoryUsage = window.performance.memory.usedJSHeapSize
    }
  }

  // 添加自定义性能标记
  public mark(name: string): void {
    if (!this.isInitialized) return

    if (!this.metrics.customMarks) {
      this.metrics.customMarks = {}
    }

    // 存储标记时间（用 performance.now()，与 measure 逻辑匹配）
    const markTime = performance.now()
    this.metrics.customMarks[name] = markTime

    // 同时添加浏览器原生标记（备用，避免自定义存储失效）
    if (performance.mark) {
      performance.mark(name)
    }

    //console.log(`✅ 已添加性能标记：${name}，时间：${Math.round(markTime)}ms`)
  }

  // 测量两个标记之间的时间
  public measure(
    name: string,
    startMark: string,
    endMark: string
  ): number | undefined {
    if (!this.isInitialized) {
      // console.error('❌ 性能监控未初始化，无法测量耗时');
      return undefined
    }

    let startTime: number | undefined
    let endTime: number | undefined

    // 1. 优先从自定义存储（customMarks）读取标记
    if (this.metrics.customMarks) {
      startTime = this.metrics.customMarks[startMark]
      endTime = this.metrics.customMarks[endMark]
      //console.log(`🔍 从自定义存储读取标记：${startMark}=${startTime}ms，${endMark}=${endTime}ms`);
    }

    // 2. 若自定义存储读取失败，降级从浏览器原生 Performance API 读取
    if (!startTime || !endTime) {
      if (performance.getEntriesByName) {
        // 读取原生标记（返回的是 PerformanceMark 数组）
        const startMarkEntry = performance.getEntriesByName(
          startMark,
          'mark'
        )[0]
        const endMarkEntry = performance.getEntriesByName(endMark, 'mark')[0]

        startTime = startMarkEntry ? startMarkEntry.startTime : undefined
        endTime = endMarkEntry ? endMarkEntry.startTime : undefined
        // console.log(`🔍 从原生API读取标记：${startMark}=${startTime}ms，${endMark}=${endTime}ms`);
      }
    }

    // 3. 计算耗时并返回
    if (startTime && endTime) {
      const duration = endTime - startTime
      // 添加原生 measure（可选，方便在浏览器 Performance 面板查看）
      if (performance.measure) {
        try {
          performance.measure(name, startMark, endMark)
        } catch (e) {
          console.error(
            `⚠️  原生 measure 失败：${
              e instanceof Error ? e.message : String(e)
            }`
          )
        }
      }
      //console.log(`✅ 测量耗时完成：${name}=${Math.round(duration)}ms`);
      return duration
    }

    // 4. 若仍失败，提示具体原因
    //console.error(`❌ 测量耗时失败：标记 ${!startTime ? startMark : endMark} 未找到`);
    return undefined
  }

  // 埋点跟踪
  public track(
    eventName: string,
    eventType: string,
    data?: TrackEventData // 使用具体类型替代any
  ): void {
    if (!this.isInitialized) return

    const event: TrackEvent = {
      eventName,
      eventType,
      timestamp: Date.now(),
      pageUrl: window.location.href,
      userId: this.userId,
      data,
    }

    this.events.push(event)
    console.log('Event tracked:', event)

    // 可以选择立即发送或批量发送
    this.sendEvent(event);
  }

  // 发送单个事件数据
  private sendEvent(event: TrackEvent): void {
    if (!this.endpoint) return

    // 使用Beacon API发送数据，不阻塞页面卸载
    if (navigator.sendBeacon) {
      const blob = new Blob([JSON.stringify(event)], {
        type: 'application/json',
      })
      navigator.sendBeacon(`${this.endpoint}/track`, blob)
    } else {
      // 降级方案：使用同步XHR
      const xhr = new XMLHttpRequest()
      xhr.open('POST', `${this.endpoint}/track`, false)
      xhr.setRequestHeader('Content-Type', 'application/json')
      xhr.send(JSON.stringify(event))
    }
  }

  // 发送所有收集的数据
  public sendData(): void {
    const performanceData = {
      metrics: this.metrics, // 页面加载/资源/内存等指标
      events: this.events, // 埋点事件（如按钮点击、Tab切换）
      env: {
        // 环境信息（方便调试）
        timestamp: Date.now(),
        userId: this.userId,
        pageUrl: window.location.href,
        userAgent: navigator.userAgent,
        browser:
          navigator.userAgent.match(/(Chrome|Firefox|Safari)\/(\d+)/)?.[0] ||
          'Unknown',
      },
    }

    // 2. 控制台格式化打印（分组+表格，清晰易读）
    console.groupCollapsed('📊 性能监控数据 - 完整报告') // 折叠分组，避免控制台杂乱
    console.log('=== 1. 环境信息 ===')
    console.table(performanceData.env) // 环境信息用表格展示

    console.log('\n=== 2. 核心性能指标 ===')
    // 处理指标为表格格式（避免undefined显示）
    const formattedMetrics = {
      '页面加载时间(ms)': performanceData.metrics.pageLoadTime || '未采集',
      '首次绘制(ms)': performanceData.metrics.firstPaint || '未采集',
      '首次内容绘制(ms)':
        performanceData.metrics.firstContentfulPaint || '未采集',
      'DOM可交互时间(ms)': performanceData.metrics.domInteractive || '未采集',
      'DOM完成时间(ms)': performanceData.metrics.domComplete || '未采集',
      '内存使用量(byte)': performanceData.metrics.memoryUsage
        ? (performanceData.metrics.memoryUsage / 1024 / 1024).toFixed(2) + 'MB'
        : '未支持',
      自定义标记数: performanceData.metrics.customMarks
        ? Object.keys(performanceData.metrics.customMarks).length
        : 0,
    }
    console.table(formattedMetrics)

    // 资源加载时间单独打印（若有数据）
    if (
      performanceData.metrics.resourceLoadTime &&
      Object.keys(performanceData.metrics.resourceLoadTime).length > 0
    ) {
      console.log('\n=== 3. 资源加载耗时 ===')
      console.table(performanceData.metrics.resourceLoadTime) // 资源名+耗时表格
    }

    // 埋点事件单独打印（若有数据）
    if (performanceData.events.length > 0) {
      console.log('\n=== 4. 埋点事件记录 ===')
      // 格式化事件数据（简化展示关键字段）
      const formattedEvents = performanceData.events.map((event) => ({
        事件名称: event.eventName,
        事件类型: event.eventType,
        触发时间: new Date(event.timestamp).toLocaleTimeString(),
        页面URL: event.pageUrl.slice(0, 50) + '...', // 截断长URL
        额外数据: event.data ? JSON.stringify(event.data) : '无',
      }))
      console.table(formattedEvents)
    }

    console.groupEnd() // 关闭分组

    // 3. 清空已打印的事件（避免重复输出）
    this.events = []
    console.log('✅ 性能数据已在控制台打印完成，已清空历史事件')
  }

  // 清除所有收集的数据
  public clearData(): void {
    this.metrics = {}
    this.events = []
  }
}

export default PerformanceMonitor.getInstance()
